use crate::error::EC_PWD_FAIL;
use crate::{
    config::{ROOT_ACCOUNT, ROOT_NAME, ROOT_PWD},
    error::{new_err_info, AppErr, SqlxErr, EC_NO_USER},
    utils::md5_encode,
};
use serde::Serialize;
use sqlx::Row;
use sqlx::{Executor, SqliteConnection};

use super::get_pool;

const CREATE_SQL: &'static str = r#"
CREATE TABLE IF NOT EXISTS tb_user (
    id INTEGER PRIMARY KEY AUTOINCREMENT, 
    name TEXT NOT NULL,
    account TEXT NOT NULL, 
    pwd_enc TEXT NOT NULL,
    UNIQUE(account)
)
"#;

pub async fn init() -> Result<(), AppErr> {
    get_pool().execute(CREATE_SQL).await?;

    let mut tx = get_pool().begin().await?;
    if !is_exists(&mut *tx, ROOT_ACCOUNT).await? {
        create_impl(&mut *tx, ROOT_NAME, ROOT_ACCOUNT, ROOT_PWD).await?;
    }
    tx.commit().await?;

    Ok(())
}

#[derive(Debug, Serialize)]
pub struct User {
    pub id: i64,
    pub name: String,
    pub account: String,
    pub pwd_enc: String,
}

pub async fn login(account: &str, pwd: &str) -> Result<User, AppErr> {
    let row = sqlx::query(
        r#"
        SELECT id, name, account, pwd_enc FROM tb_user WHERE account = ? LIMIT 1
    "#,
    )
    .bind(account)
    .fetch_optional(get_pool())
    .await?
    .ok_or(new_err_info(EC_NO_USER, "没有该用户"))?;

    let user = User {
        id: row.get(0),
        name: row.get(1),
        account: row.get(2),
        pwd_enc: row.get(3),
    };

    let pwd_enc = md5_encode(pwd);
    if user.pwd_enc != pwd_enc {
        return Err(new_err_info(EC_PWD_FAIL, "密码错误"));
    }

    Ok(user)
}

async fn create_impl(
    conn: &mut SqliteConnection,
    name: &str,
    account: &str,
    pwd: &str,
) -> Result<i64, SqlxErr> {
    let pwd_enc = md5_encode(pwd);

    let row = sqlx::query(
        r#"
        INSERT INTO tb_user (name, account, pwd_enc) VALUES (?, ?, ?)
    "#,
    )
    .bind(name)
    .bind(account)
    .bind(&pwd_enc)
    .execute(conn)
    .await?;

    Ok(row.last_insert_rowid())
}

async fn is_exists(conn: &mut SqliteConnection, account: &str) -> Result<bool, SqlxErr> {
    let row = sqlx::query(r#"SELECT id FROM tb_user WHERE account = ? LIMIT 1"#)
        .bind(account)
        .fetch_optional(conn)
        .await?;
    Ok(row.is_some())
}
